package com.kuizii.base.common.configs;


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.kuizii.base.common.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;

import static java.util.Collections.singletonMap;


@ConditionalOnClass(value = RedisTemplate.class)
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @ConditionalOnMissingBean
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisConfiguration redisConfiguration = new RedisStandaloneConfiguration("127.0.0.1");
        LettuceConnectionFactory redisConnectionFactory = new LettuceConnectionFactory(redisConfiguration);

        return redisConnectionFactory;
    }

    @ConditionalOnMissingBean
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate redisTemplate = new StringRedisTemplate(factory);
        redisTemplate.setConnectionFactory(factory);

        return redisTemplate;
    }


    @Qualifier("redisTemplate")
    @Bean(name = "redisTemplate")
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(factory);

        RedisSerializer jackson2JsonRedisSerializer = getRedisSerializer();

        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        return redisTemplate;
    }

    /**
     * 指定使用哪一种缓存
     *
     * @param
     * @return
     */
    @ConditionalOnMissingBean(value = CacheManager.class)
    @Bean
    @Primary
    public CacheManager cacheManager(RedisConnectionFactory factory) {

        //为考虑突发热点数据缓存在过期后击穿，需要在每次取缓存时续期等操作，存值时加随机数延长时间 防雪崩可能
        RedisCacheWriter redisCacheWriter = new RandomRedisCacheWriter(factory);

        return RedisCacheManager.builder(redisCacheWriter)
                .cacheDefaults(redisCacheConfiguration())
                //.withInitialCacheConfigurations(singletonMap("key1", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues()))
                //.withInitialCacheConfigurations(singletonMap("key2", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues()))
                .build();
    }

    @ConditionalOnMissingBean(value = RedisCacheConfiguration.class)
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {

        //配置默认过期时间 为1小时，实际上加了随机数延长时间 防雪崩可能
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        configuration = configuration
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(getRedisSerializer()))
                .entryTtl(Duration.ofHours(1L));
        return configuration;
    }


    private RedisSerializer getRedisSerializer() {
        // 使用Jackson2JsonRedisSerialize 替换默认序列化(默认采用的是JDK序列化)
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);

        jackson2JsonRedisSerializer.setObjectMapper(om);

        return jackson2JsonRedisSerializer;
    }

    @ConditionalOnMissingBean(RedisUtil.class)
    @Bean
    public RedisUtil getRedisUtil() {
        return new RedisUtil();
    }
}

